package com.toy.core.security.login.web;

import static com.google.common.base.Strings.isNullOrEmpty;
import static org.apache.commons.lang.StringUtils.isNotBlank;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.StringTokenizer;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.xml.sax.SAXException;

import com.toy.core.config.SysConfig;
import com.toy.core.security.AuthenticatedType;
import com.toy.core.security.Authentication;
import com.toy.core.security.UsernamePasswordAuthentication;
import com.toy.core.security.authenticate.AuthenticationProvider;
import com.toy.core.security.exception.AuthenticationException;
import com.toy.core.util.CookieUtils;
import com.toy.core.web.BaseController;

import edu.yale.its.tp.cas.client.ProxyTicketValidator;
import edu.yale.its.tp.cas.client.Util;

@Controller
@RequestMapping("/client/cas")
public class CasController extends BaseController{
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(CasController.class);
	
	@Autowired
	private AuthenticationProvider authenticationProvider;
	
	@Autowired
	private SysConfig sysConfig;
	
	private static String CAS_SESSION_TICKET_KEY = "CAS_SESSION_TIECKT_KEY";
	
	@RequestMapping("/login")
	public String login(HttpServletRequest request,HttpServletResponse response) throws Exception{
		String casTicket = request.getParameter("ticket");
		casTicket = isNullOrEmpty(casTicket)?(String)request.getSession().getAttribute(CAS_SESSION_TICKET_KEY):null;
		if (isNullOrEmpty(casTicket)){
			String casLoginUrl = sysConfig.getCasLoginUrl() + "?service="+ getService(request)+"&renew=true";	
			response.sendRedirect(casLoginUrl);
			return null;
		}else{
			request.getSession().setAttribute(CAS_SESSION_TICKET_KEY, casTicket);
			String casUser = getAuthenticatedUser(request,casTicket);
			UsernamePasswordAuthentication authentication = new UsernamePasswordAuthentication(casUser,AuthenticatedType.CAS);
			authentication.setAttribute(Authentication.ATTR_REMOTEADDR, request.getRemoteAddr());
			authentication.setAttribute(Authentication.ATTR_SESSIONID, request.getSession().getId());
			try {
				Authentication newAuthentication = authenticationProvider.authenticate(authentication);
				CookieUtils.saveAuthentication(newAuthentication, request, response);
			} catch (AuthenticationException e) {
				if (logger.isDebugEnabled()) {logger.debug("AuthenticationException=" + e);}
				request.setAttribute("error", e.getMessage());
				StringBuffer scripts = new StringBuffer();
				scripts.append("alert(\"系统提示:"+e.getMessage()+"\");");
				scripts.append("location.href='"+request.getContextPath() + sysConfig.getCasLoginValidateUrl()+"';");
				super.writeScripts(response, scripts.toString());
				return null;
			}
			
			String target = request.getParameter("target");
			if (isNotBlank(target)){
				return "redirect:"+target;
			}
			
			return "redirect:"+sysConfig.getHomePageUrl();
		}
	}
	/*
	 * Converts a ticket parameter to a username, taking into account an
     * optionally configured trusted proxy in the tier immediately in front
     * of us.
     */
    private String getAuthenticatedUser(HttpServletRequest request,String ticket)
        throws ServletException {
        ProxyTicketValidator pv = null;
        String casAuthorizedProxy = null;
        try {
            pv = new ProxyTicketValidator();
            pv.setCasValidateUrl(sysConfig.getCasValidateUrl());
            pv.setServiceTicket(ticket);
            pv.setService(getService(request));
            //pv.setRenew(Boolean.valueOf(casRenew).booleanValue());
            //参考http://tp.its.yale.edu/pipermail/cas/2004-April/000433.html
            //当用于重定向到Cas登陆时设置renew=true,当用于验证ticket时,renew=false
            pv.setRenew(false);
            pv.validate();
            if (!pv.isAuthenticationSuccesful())
                throw new ServletException(
                    "CAS authentication error: " + pv.getErrorCode() + ": " + pv.getErrorMessage());
            if (pv.getProxyList().size() != 0) {
                // ticket was proxied
                if (casAuthorizedProxy == null) {
                    throw new ServletException("this page does not accept proxied tickets");
                } else {
                    boolean authorized = false;
                    String proxy = (String)pv.getProxyList().get(0);
                    StringTokenizer casProxies =
                        new StringTokenizer(casAuthorizedProxy);
                    while (casProxies.hasMoreTokens()) {
                        if (proxy.equals(casProxies.nextToken())) {
                            authorized = true;
                            break;
                        }
                    }
                    if (!authorized) {
                        throw new ServletException(
                            "unauthorized top-level proxy: '"
                                + pv.getProxyList().get(0)
                                + "'");
                    }
                }
            }
            return pv.getUser();
        } catch (SAXException ex) {
            String xmlResponse = "";
            if (pv != null)
                xmlResponse = pv.getResponse();
            throw new ServletException(ex + " " + xmlResponse);
        } catch (ParserConfigurationException ex) {
            throw new ServletException(ex);
        } catch (IOException ex) {
            throw new ServletException(ex);
        }
    }

    /**
     * Returns either the configured service or figures it out for the current
     * request.  The returned service is URL-encoded.
     * @throws UnsupportedEncodingException 
     */
    private String getService(HttpServletRequest request)
        throws ServletException {
    	String target = request.getServerName()+":"+request.getServerPort();
    	return Util.getService(request,target);
    }
}
